﻿using System;
using System.Reflection;
using System.Linq.Expressions;
using System.Linq;
using System.Runtime.Serialization;

namespace ExpressionSerialization
{
  [DataContract]
  public class XMemberExpression : XExpression
  {
    [DataMember]
    public XExpression Expression { get; set; }
    [DataMember]
    public string MemberName { get; set; }
    [DataMember] //todo:null nepřenášet
    public object Value { get; set; } // používám, když měním na konstantu


    public override void FromExpression(Expression source_)
    {
      //todo: zkusit zapsat přímo konstantu
      var exp = source_ as MemberExpression;
      // member accesses off of constant's are common, and yet since these partial evals
      // are never re-used, using reflection to access the member is faster than compiling  
      // and invoking a lambda
      var ce = exp.Expression as ConstantExpression;
      if(ce != null) {
        // evaluate - toto nefunguje na lokální proměnné
        //var ooo = exp.Member.GetValue(ce.Value);
        var ss = System.Linq.Expressions.Expression.Convert(exp,typeof(object));
        Expression<Func<object>> lambda = System.Linq.Expressions.Expression.Lambda<Func<object>>(ss);
        Func<object> fn = lambda.Compile();
        object value = fn();
         // base
        TypeName = ReflectionExtensions.ToSerializableForm(value.GetType());
        NodeType = ExpressionType.Constant;
        Value = value;


      } else {
        base.FromExpression(source_);
        Expression = XExpression.CreateFromExpression((source_ as MemberExpression).Expression);
        MemberName = exp.Member.DeclaringType.ToSerializableForm() + "|" + exp.Member.ToString();
      }
    }

    public override Expression ToExpression(Type dataContract_, System.Collections.Generic.List<ParameterExpression> parameters_)
    {
      if(NodeType == ExpressionType.Constant) {
        return System.Linq.Expressions.Expression.Constant(Value);
      } else {
        Expression expression = null;
        if(Expression != null) expression = Expression.ToExpression(dataContract_, parameters_);

        string[] fullName = MemberName.Split('|');
        string name = fullName[1];
        MemberInfo member = (from m in ReflectionExtensions.TypeFromSerializableForm(fullName[0], dataContract_).GetMembers()
                             where m.ToString() == name
                             select m).First();

        return System.Linq.Expressions.Expression.MakeMemberAccess(expression, member);
      }
    }

  }
}
